package com.huaxonline.sign.sdk;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 完全独立的签名SDK客户端
 * 不依赖任何第三方库，只使用JDK标准库
 * 
 * @author zhaoshulei
 * @since 2023/9/13
 */
public class SignClient {
    
    // 请求头常量
    public static final String HEADER_APP_ID = "AppId";
    public static final String HEADER_TIMESTAMP = "Timestamp";
    public static final String HEADER_NONCE = "Nonce";
    public static final String HEADER_SIGNATURE = "Signature";
    public static final String HEADER_HASHED_REQUEST_PAYLOAD = "HashedRequestPayload";
    
    // 签名算法
    public enum SignAlgorithm {
        SHA256("SHA-256"),
        MD5("MD5"),
        HMAC_SHA256("HmacSHA256");
        
        private final String algorithmName;
        
        SignAlgorithm(String algorithmName) {
            this.algorithmName = algorithmName;
        }
        
        public String getAlgorithmName() {
            return algorithmName;
        }
    }
    
    private final String appId;
    private final String secret;
    private final SignAlgorithm algorithm;
    private final long timestampDisparity;
    private final boolean debugMode;
    
    /**
     * 构造函数
     */
    public SignClient(String appId, String secret) {
        this(appId, secret, SignAlgorithm.SHA256, 900000L, false);
    }
    
    /**
     * 构造函数（指定算法）
     */
    public SignClient(String appId, String secret, SignAlgorithm algorithm) {
        this(appId, secret, algorithm, 900000L, false);
    }
    
    /**
     * 完整构造函数
     */
    public SignClient(String appId, String secret, SignAlgorithm algorithm, long timestampDisparity, boolean debugMode) {
        if (appId == null || appId.trim().isEmpty()) {
            throw new IllegalArgumentException("AppId不能为空");
        }
        if (secret == null || secret.trim().isEmpty()) {
            throw new IllegalArgumentException("Secret不能为空");
        }
        
        this.appId = appId.trim();
        this.secret = secret.trim();
        this.algorithm = algorithm;
        this.timestampDisparity = timestampDisparity;
        this.debugMode = debugMode;
    }
    
    /**
     * 生成签名参数
     */
    public SignParams generateSignParams() {
        long timestamp = System.currentTimeMillis();
        String nonce = generateNonce();
        return new SignParams(appId, timestamp, nonce);
    }
    
    /**
     * 生成签名
     */
    public String generateSign(Map<String, Object> params) {
        Map<String, Object> signParams = new TreeMap<>(params);
        signParams.put(HEADER_APP_ID, appId);
        signParams.put(HEADER_TIMESTAMP, params.get(HEADER_TIMESTAMP));
        signParams.put(HEADER_NONCE, params.get(HEADER_NONCE));
        
        String signString = buildSignString(signParams);
        return sign(signString, secret);
    }
    
    /**
     * 生成签名（包含请求体）
     */
    public String generateSignWithBody(Map<String, Object> params, String body) {
        Map<String, Object> signParams = new TreeMap<>(params);
        
        if (body != null && !body.trim().isEmpty()) {
            String bodyHash = sign(body, "");
            signParams.put(HEADER_HASHED_REQUEST_PAYLOAD, bodyHash);
        }
        
        return generateSign(signParams);
    }
    
    /**
     * 发送GET请求
     */
    public HttpResponse sendGet(String url, Map<String, Object> params) throws IOException {
        SignParams signParams = generateSignParams();
        Map<String, Object> requestParams = new HashMap<>(params);
        requestParams.put(HEADER_TIMESTAMP, signParams.getTimestamp());
        requestParams.put(HEADER_NONCE, signParams.getNonce());
        
        String signature = generateSign(requestParams);
        
        // 构建URL
        StringBuilder urlBuilder = new StringBuilder(url);
        if (!params.isEmpty()) {
            urlBuilder.append("?");
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (!isSignHeader(entry.getKey())) {
                    urlBuilder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()))
                            .append("=")
                            .append(URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8.name()))
                            .append("&");
                }
            }
            // 移除最后的&
            if (urlBuilder.charAt(urlBuilder.length() - 1) == '&') {
                urlBuilder.setLength(urlBuilder.length() - 1);
            }
        }
        
        HttpURLConnection connection = createConnection(urlBuilder.toString(), "GET");
        setSignHeaders(connection, signParams, signature);
        
        if (debugMode) {
            System.out.println("发送GET请求: " + urlBuilder.toString());
        }
        
        return executeRequest(connection);
    }
    
    /**
     * 发送POST请求
     */
    public HttpResponse sendPost(String url, Map<String, Object> params, String body) throws IOException {
        SignParams signParams = generateSignParams();
        Map<String, Object> requestParams = new HashMap<>(params);
        requestParams.put(HEADER_TIMESTAMP, signParams.getTimestamp());
        requestParams.put(HEADER_NONCE, signParams.getNonce());
        
        String signature = generateSignWithBody(requestParams, body);
        
        HttpURLConnection connection = createConnection(url, "POST");
        setSignHeaders(connection, signParams, signature);
        
        // 设置请求体
        if (body != null && !body.trim().isEmpty()) {
            connection.setDoOutput(true);
            try (OutputStream os = connection.getOutputStream()) {
                os.write(body.getBytes(StandardCharsets.UTF_8));
            }
        }
        
        if (debugMode) {
            System.out.println("发送POST请求: " + url);
            System.out.println("请求体: " + body);
        }
        
        return executeRequest(connection);
    }
    
    /**
     * 发送POST请求（JSON）
     */
    public HttpResponse sendPostJson(String url, Object data) throws IOException {
        String jsonBody = toJson(data);
        return sendPost(url, new HashMap<>(), jsonBody);
    }
    
    /**
     * 验证时间戳
     */
    public boolean isValidTimestamp(long timestamp) {
        long disparity = Math.abs(System.currentTimeMillis() - timestamp);
        return disparity <= timestampDisparity;
    }
    
    /**
     * 构建签名参数字符串
     */
    private String buildSignString(Map<String, Object> params) {
        StringBuilder sb = new StringBuilder();
        sb.append("Secret=").append(secret).append("&");
        
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (entry.getValue() != null && !String.valueOf(entry.getValue()).trim().isEmpty()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 1); // 移除最后的&
        }
        
        String signString = sb.toString();
        if (debugMode) {
            System.out.println("签名字符串: " + signString);
        }
        
        return signString;
    }
    
    /**
     * 执行签名
     */
    private String sign(String data, String key) {
        try {
            switch (algorithm) {
                case SHA256:
                    return sha256(data);
                case MD5:
                    return md5(data);
                case HMAC_SHA256:
                    return hmacSha256(data, key);
                default:
                    return sha256(data);
            }
        } catch (Exception e) {
            throw new RuntimeException("签名计算失败", e);
        }
    }
    
    /**
     * SHA256签名
     */
    private String sha256(String data) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hash);
    }
    
    /**
     * MD5签名
     */
    private String md5(String data) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hash);
    }
    
    /**
     * HMAC-SHA256签名
     */
    private String hmacSha256(String data, String key) throws Exception {
        javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256");
        javax.crypto.spec.SecretKeySpec secretKeySpec = new javax.crypto.spec.SecretKeySpec(
                key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hash);
    }
    
    /**
     * 字节数组转十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
    
    /**
     * 生成随机nonce
     */
    private String generateNonce() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    
    /**
     * 创建HTTP连接
     */
    private HttpURLConnection createConnection(String url, String method) throws IOException {
        URL urlObj = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
        connection.setRequestMethod(method);
        connection.setConnectTimeout(30000); // 30秒连接超时
        connection.setReadTimeout(60000);    // 60秒读取超时
        connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
        connection.setRequestProperty("User-Agent", "SignClient/1.0");
        return connection;
    }
    
    /**
     * 设置签名请求头
     */
    private void setSignHeaders(HttpURLConnection connection, SignParams signParams, String signature) {
        connection.setRequestProperty(HEADER_APP_ID, appId);
        connection.setRequestProperty(HEADER_TIMESTAMP, String.valueOf(signParams.getTimestamp()));
        connection.setRequestProperty(HEADER_NONCE, signParams.getNonce());
        connection.setRequestProperty(HEADER_SIGNATURE, signature);
    }
    
    /**
     * 执行请求
     */
    private HttpResponse executeRequest(HttpURLConnection connection) throws IOException {
        int responseCode = connection.getResponseCode();
        
        String responseBody;
        try (InputStream inputStream = responseCode >= 200 && responseCode < 300 
                ? connection.getInputStream() : connection.getErrorStream()) {
            responseBody = readInputStream(inputStream);
        }
        
        Map<String, String> headers = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
            if (entry.getKey() != null && entry.getValue() != null && !entry.getValue().isEmpty()) {
                headers.put(entry.getKey(), entry.getValue().get(0));
            }
        }
        
        return new HttpResponse(responseCode, responseBody, headers);
    }
    
    /**
     * 读取输入流
     */
    private String readInputStream(InputStream inputStream) throws IOException {
        if (inputStream == null) {
            return "";
        }
        
        StringBuilder result = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
        }
        return result.toString();
    }
    
    /**
     * 判断是否为签名头
     */
    private boolean isSignHeader(String key) {
        return HEADER_APP_ID.equals(key) ||
               HEADER_TIMESTAMP.equals(key) ||
               HEADER_NONCE.equals(key) ||
               HEADER_SIGNATURE.equals(key) ||
               HEADER_HASHED_REQUEST_PAYLOAD.equals(key);
    }
    
    /**
     * 简单的JSON序列化（仅支持基本类型和Map）
     */
    private String toJson(Object obj) {
        if (obj == null) {
            return "null";
        }
        
        if (obj instanceof String) {
            return "\"" + escapeJson((String) obj) + "\"";
        }
        
        if (obj instanceof Number || obj instanceof Boolean) {
            return obj.toString();
        }
        
        if (obj instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) obj;
            StringBuilder sb = new StringBuilder("{");
            boolean first = true;
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                if (!first) {
                    sb.append(",");
                }
                sb.append("\"").append(escapeJson(String.valueOf(entry.getKey()))).append("\":")
                  .append(toJson(entry.getValue()));
                first = false;
            }
            sb.append("}");
            return sb.toString();
        }
        
        // 对于其他类型，使用toString()
        return "\"" + escapeJson(obj.toString()) + "\"";
    }
    
    /**
     * JSON字符串转义
     */
    private String escapeJson(String str) {
        if (str == null) {
            return "";
        }
        return str.replace("\\", "\\\\")
                  .replace("\"", "\\\"")
                  .replace("\b", "\\b")
                  .replace("\f", "\\f")
                  .replace("\n", "\\n")
                  .replace("\r", "\\r")
                  .replace("\t", "\\t");
    }
    
    /**
     * 签名参数类
     */
    public static class SignParams {
        private final String appId;
        private final long timestamp;
        private final String nonce;
        
        public SignParams(String appId, long timestamp, String nonce) {
            this.appId = appId;
            this.timestamp = timestamp;
            this.nonce = nonce;
        }
        
        public String getAppId() { return appId; }
        public long getTimestamp() { return timestamp; }
        public String getNonce() { return nonce; }
    }
    
    /**
     * HTTP响应类
     */
    public static class HttpResponse {
        private final int statusCode;
        private final String body;
        private final Map<String, String> headers;
        
        public HttpResponse(int statusCode, String body, Map<String, String> headers) {
            this.statusCode = statusCode;
            this.body = body;
            this.headers = headers;
        }
        
        public int getStatusCode() { return statusCode; }
        public String getBody() { return body; }
        public Map<String, String> getHeaders() { return headers; }
        
        public boolean isSuccess() {
            return statusCode >= 200 && statusCode < 300;
        }
        
        @Override
        public String toString() {
            return "HttpResponse{" +
                    "statusCode=" + statusCode +
                    ", body='" + body + '\'' +
                    ", headers=" + headers +
                    '}';
        }
    }
}